#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/Analysis/CFGPrinter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"

#include <clang/CodeGen/CodeGenAction.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/CompilerInvocation.h>
#include <clang/Basic/DiagnosticOptions.h>
#include <clang/Basic/TargetInfo.h>
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "llvm/Support/Host.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/AST/ASTConsumer.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/Parse/ParseAST.h"
#include "clang/AST/ASTContext.h"

#include<iostream>
#include <vector>

std::unique_ptr<llvm::Module> ir_gen(char * filename, llvm::LLVMContext& llvm_ctx) {

	std::vector<const char *> args;
	args.push_back( "-emit-llvm" );
	args.push_back( filename );

	clang::CompilerInstance Clang;
	// Clang.createDiagnostics();

	llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> p_diag_option(new clang::DiagnosticOptions());
	clang::TextDiagnosticPrinter* p_diag_printer = new clang::TextDiagnosticPrinter(llvm::outs(), p_diag_option.get());
	llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> p_diag_ids(new clang::DiagnosticIDs());

	clang::DiagnosticsEngine* pDiagnosticsEngine = new clang::DiagnosticsEngine(p_diag_ids,
			p_diag_option,
			p_diag_printer);

	Clang.setDiagnostics(pDiagnosticsEngine);

	std::shared_ptr<clang::CompilerInvocation> CI(new clang::CompilerInvocation());
	llvm::ArrayRef<const char *>array = llvm::ArrayRef<const char *>(args);
	clang::CompilerInvocation::CreateFromArgs( *CI.get(), array,
			*pDiagnosticsEngine);
	Clang.setInvocation(CI);
	clang::CodeGenAction *action = new clang::EmitLLVMOnlyAction(&llvm_ctx);

	if (!Clang.ExecuteAction(*action))
		return nullptr;

	std::unique_ptr<llvm::Module> module = action->takeModule();

	// return std::move(module);
	return module;
}

int main(int argc, char** argv) {
	if( argc != 2 ) {
		return -1;
	}

	char* cpp_file = argv[1];
	llvm::LLVMContext llvm_ctx;
	std::unique_ptr<llvm::Module> m = ir_gen(cpp_file, llvm_ctx);

	m->print(llvm::outs(), nullptr);
	return 0;
}
